﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using log4net;
using System.Web.Script.Serialization;

namespace Et.Open.Socket.Demo.core
{
    public class TcpServer
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(TcpServer));

        private const int SocketTimeout = 10000;

        private IPAddress ip = IPAddress.Any;

        private int port = 35580;

        private IApiProcessor processor = new EmptyApiProcessor();

        private string pid = "";

        private TcpListener listener;

        private Thread listenThread;

        private bool started = false;

        private JavaScriptSerializer ser = new JavaScriptSerializer();

        private static TcpServer instance = null;

        private static TcpServer Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new TcpServer();
                }

                return instance;
            }
        }

        public static string Pid => Instance.pid;

        public static int Port => Instance.port;

        private TcpServer()
        {
        }

        private void StartServer()
        {
            if (started)
            {
                throw new InvalidOperationException("tcp server already started");
            }

            listener = new TcpListener(ip, port);
            listener.Start();
            listenThread = new Thread(ListenApiRequest)
            {
                IsBackground = true
            };
            listenThread.Start();
            started = true;
        }

        private void ListenApiRequest()
        {
            while (true)
            {
                System.Net.Sockets.Socket socket = listener.AcceptSocket();
                socket.ReceiveTimeout = 10000;
                socket.SendTimeout = 10000;
                ProcessRequest(socket);
            }
        }

        private void ProcessRequest(System.Net.Sockets.Socket sock)
        {
            Thread thread = new Thread((ThreadStart)delegate
            {
                SocketProtocolWrapper socketProtocolWrapper = new SocketProtocolWrapper(sock, pid);
                try
                {
                    ApiData data = socketProtocolWrapper.ReceiveRequestData();
                    ApiData resp = null;
                    lock (processor)
                    {
                        resp = processor.Process(data);
                    }

                    socketProtocolWrapper.SendResponseData(resp);
                    byte[] array = new byte[3];
                }
                catch (EtSocketDataValidationException ex)
                {
                    log.Warn((object)"invalid socket request data", (Exception)ex);
                    socketProtocolWrapper.SendResponseData(new ApiData
                    {
                        DataType = ushort.MaxValue,
                        Data = ser.Serialize(new ApiRespBase
                        {
                            errorCode = 126,
                            errorMsg = "请求数据验证失败：" + ex.Message
                        })
                    });
                }
                catch (Exception ex2)
                {
                    log.Error((object)"fail to process socket api request", ex2);
                }
                finally
                {
                    try
                    {
                        sock.Shutdown(SocketShutdown.Both);
                    }
                    catch
                    {
                    }

                    try
                    {
                        sock.Close();
                    }
                    catch
                    {
                    }

                    try
                    {
                        sock.Dispose();
                    }
                    catch
                    {
                    }
                }
            });
            thread.IsBackground = true;
            thread.Start();
        }

        private void StopServer()
        {
            listenThread.Abort();
            listener.Stop();
            started = false;
        }

        private void ConfigureServer(string pid, IApiProcessor processor, int port, string ip)
        {
            if (started)
            {
                throw new InvalidOperationException("tcp server already started");
            }

            this.pid = pid;
            if (processor != null)
            {
                this.processor = processor;
            }

            if (port != 0)
            {
                this.port = port;
            }

            if (!string.IsNullOrWhiteSpace(ip))
            {
                this.ip = IPAddress.Parse(ip);
            }
        }

        public static bool Configure(string pid, IApiProcessor processor = null, int port = 0, string ip = "")
        {
            try
            {
                Instance.ConfigureServer(pid, processor, port, ip);
                return true;
            }
            catch (Exception ex)
            {
                log.Error((object)"fail to configure server", ex);
                return false;
            }
        }

        public static bool Start()
        {
            try
            {
                Instance.StartServer();
                log.Info((object)"tcp api server started");
                return true;
            }
            catch (Exception ex)
            {
                log.Error((object)"fail to start tcp api server", ex);
                return false;
            }
        }

        public static bool Stop()
        {
            try
            {
                Instance.StopServer();
                log.Info((object)"tcp api server stopped");
                return true;
            }
            catch (Exception ex)
            {
                log.Error((object)"fail to stop tcp api server", ex);
                return false;
            }
        }
    }
}
